import { message, type UploadProps } from "ant-design-vue";
import { reactive, watch } from "vue";
import type { FileState, Options, State, UploadState } from "./typing";
import {
  del,
  downloadFile,
  mergeUpload,
  queryChunk,
  queryFileList,
  uploadChunk,
} from "@/api/file";
import SparkMD5 from "spark-md5";
import { throttle } from "lodash";
import { useI18n } from "vue-i18n";
import type { FileItem } from "@/api/file/typing";
// 图片文件类型
const imageType = [
  "jpeg",
  "jfif",
  "xbm",
  "ico",
  "tiff",
  "bmp",
  "webp",
  "bmp",
  "jpg",
  "png",
  "tif",
  "gif",
  "pcx",
  "tga",
  "exif",
  "fpx",
  "svg",
  "psd",
  "cdr",
  "pcd",
  "dxf",
  "ufo",
  "eps",
  "ai",
  "raw",
  "WMF",
  "webp",
  "avif",
  "apng",
];

export const useUpload = (options: Options) => {
  const { t } = useI18n();
  // 已上传文件状态
  const state = reactive<State>({
    loading: false,
    fileList: [],
  });

  // 初始上传状态
  const initUploadState = (): UploadState => ({
    total: 0,
    valid: true,
    success: 0,
    stateList: [],
    mergeQueue: [],
    mergeState: false,
  });
  const uploadState = reactive(initUploadState());

  /**上传之前 */
  const beforeUpload: UploadProps["beforeUpload"] = async (file, fileList) => {
    // 单文件上传成功时候需要删除原来的文件
    if (!options.multiple && state.fileList.length) {
      await deleteFile(state.fileList[0].fileId);
    }
    return new Promise((resolve, reject) => {
      // 上传文件总数
      uploadState.total = fileList.length;
      // 文件数量校验
      if (uploadState.total + state.fileList.length > options.maxFileNums) {
        message.warning(
          `最多还可上传${options.maxFileNums - state.fileList.length}个文件`
        );
        uploadState.valid = false;
        return reject();
      }
      // 文件大小-kb
      const fileSize = file.size / 1024;
      // 文件后缀
      const fileType = file.name.split(".").pop();
      // 校验文件大小
      if (fileSize > options.maxFileSize) {
        message.warning(`${file.name}: 超出文件大小`);
        uploadState.valid = false;
        return reject();
      }
      // 校验文件类型
      else if (options.accept != "*") {
        if (
          (options.accept == "image/*" && !imageType.includes(fileType)) ||
          (options.accept != "image/*" &&
            !options.accept.split(",").includes(fileType))
        ) {
          message.warning(`${file.name}：不支持该类型文件`);
          uploadState.valid = false;
          return reject();
        }
      }

      // 分片
      // 切片读文件需要时间，开启loading
      state.loading = true;
      const chunkList = [];
      const chunSizeByte = options.chunkSize * 1024;
      for (let i = 0; i < file.size; i += chunSizeByte) {
        chunkList.push(
          file.slice(i, chunSizeByte > file.size ? file.size : i + chunSizeByte)
        );
      }
      const fileReader = new FileReader();
      const hash = new SparkMD5.ArrayBuffer();
      fileReader.onload = () => {
        hash.append(fileReader.result as ArrayBuffer);
        // 更新上传节流处理
        const updateSchedule = throttle((fileState: FileState) => {
          fileState.schedule = +(
            (fileState.success / fileState.chunkList.length) *
            100
          ).toFixed(2);
        }, 0);
        uploadState.stateList.push({
          fileMd5: hash.end(),
          chunkList,
          success: 0,
          schedule: 0,
          originFileName: file.name,
          updateSchedule,
        });
        resolve();
      };
      fileReader.readAsArrayBuffer(file);
    });
  };

  /**上传 */
  const customRequest: UploadProps["customRequest"] = async ({ file }) => {
    // 等待所有文件一起上传
    if (--uploadState.total) return;
    uploadState.total = uploadState.stateList.length;
    state.loading = false;
    for (const fileState of uploadState.stateList) {
      uploadFile(fileState, file as File);
    }
  };

  /**上传单个文件 */
  const uploadFile = async (fileState: FileState, file: File) => {
    // 查询是否为断点续传
    const { success, result } = await queryChunk({
      chunkName: fileState.fileMd5,
    });
    if (success) {
      fileState.success = result.uploadIndexList.length;
      const index = result.uploadIndexList.pop() || 0;
      const extname = file.name.split(".").pop();
      uploadFileChunk(fileState, index, extname);
    }
  };

  /**上传分片 */
  const uploadFileChunk = async (
    fileState: FileState,
    index: number,
    extname: string
  ) => {
    // 所有分片都已经上传-直接合并
    if (fileState.success == fileState.chunkList.length) {
      uploadState.mergeQueue.push(async () => await merge(fileState, extname));
      handleMerge();
      return;
    }
    const chunk = fileState.chunkList[index];
    const formdata = new FormData();
    formdata.append("chunk", chunk);
    formdata.append("name", `${fileState.fileMd5}@${index}`);
    formdata.append("filename", fileState.fileMd5);
    uploadChunk(formdata).then(({ success }) => {
      if (success) {
        fileState.success++;
        fileState.updateSchedule(fileState);
        uploadFileChunk(fileState, index + 1, extname);
      }
    });
  };

  /**合并分片 */
  const merge = async (fileState: FileState, extname: string) => {
    const { success, result } = await mergeUpload({
      filename: fileState.fileMd5,
      extname,
      originFileName: fileState.originFileName,
      type: options.multiple ? "multiple" : "radio",
      fileHeaderId: options.fileHeaderId,
    });
    if (success) {
      // 将上传完的文件从上传状态->文件状态
      state.fileList.push(result);
      const index = uploadState.stateList.findIndex(
        (f) => f.fileMd5 == fileState.fileMd5
      );
      uploadState.stateList.splice(index, 1);
      // 抛出文件组ID
      options.onEmit?.(result.fileHeaderId);
    }
    uploadState.success++;
    if (uploadState.success == uploadState.total) {
      // 所有文件都上传成功-清空上传状态
      Object.assign(uploadState, initUploadState());
    }
  };

  /**查询文件列表 */
  const query = async () => {
    state.loading = true;
    const { success, result } = await queryFileList(options.fileHeaderId);
    state.loading = false;
    if (success) {
      state.fileList = result.map((m) => {
        const file = state.fileList.find((f) => f.fileId == m.fileId);
        m.loaded = file?.loaded || false;
        return m;
      });
    }
  };

  /**删除文件 */
  const deleteFile = async (fileId: string) => {
    if (!fileId) {
      return message.warning("文件ID为空");
    }
    state.loading = true;
    const { success } = await del(fileId);
    state.loading = false;
    if (success) {
      if (options.multiple) {
        message.success(t("删除成功"));
      }
      state.fileList = state.fileList.filter((f) => f.fileId != fileId);
      // 文件列表为空-清空文件头ID
      if (!state.fileList.length) {
        options.onEmit?.(null);
      }
    }
  };

  /**下载文件 */
  const download = async (file: FileItem) => {
    if (!file) return message.warning("文件ID不存在");
    const res = await downloadFile(file.fileId);
    if (res instanceof Blob) {
      const href = URL.createObjectURL(res);
      const a = document.createElement("a");
      a.download = file.originFileName;
      a.href = href;
      a.style.display = "none";
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
    }
  };

  /**取一个合并任务合并 */
  const handleMerge = () => {
    if (uploadState.mergeState) return;
    uploadState.mergeState = true;
    // 取出合并任务
    const fn = uploadState.mergeQueue.pop();
    fn().then(() => {
      uploadState.mergeState = false;
      if (uploadState.mergeQueue.length) handleMerge();
    });
  };

  watch(
    () => options.fileHeaderId,
    (n, o) => {
      if (n && n != o) {
        query();
      } else {
        state.fileList = [];
      }
    },
    { immediate: true }
  );

  return {
    state,
    uploadState,
    beforeUpload,
    customRequest,
    deleteFile,
    download,
  };
};
